/** 等高*/
import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
import img1 from "@/assets/imgs/1.jpg"
import { FrameSty, ListItemSty, ListScrollBar, ListSty } from './style'

export default function VirtualScroll() {
  const [listItemDefaultHeight, setListItemDefaultHeight] = useState(50)
  /** 滑块DOM*/
  const scrollBar = useRef<any>(null)
  /** 滑块样式*/
  const [scrollBarStyle, setScrollBarStyle] = useState<React.CSSProperties>({})
  /** 列表样式*/
  const [listStyle, setListStyle] = useState<React.CSSProperties>({})
  /** 滑块是否可以移动*/
  const isMove = useRef(false);
  /** 列表在视口的坐标*/
  const initPointerObj = useRef({ x: 0, y: 0 })
  /** 鼠标点击时在滑块中的坐标*/
  const mouseInBar = useRef({ x: 0, y: 0 })
  const frameDom = useRef(null)
  /** 列表可渲染数量*/
  const renderQuantity = useRef(40)
  /** 列表DOM*/
  const listDom = useRef(null)
  /** 列表可见区域高度*/
  const listVisualHeight = useRef(0);
  /** 数据开始下标*/
  const [startInd, setStartInd] = useState(0);
  /** 总数据*/
  const [allList, setAllList] = useState<any[]>([])
  /** 渲染数据*/
  const [resultList, setResultList] = useState<any[]>([]);

  /** 列表数量*/
  const listNum = useMemo(() => {
    return allList.length
  }, [allList])

  /** 列表实际高度*/
  const listActualHeight = useMemo(() => {
    return listItemDefaultHeight * listNum
  }, [listItemDefaultHeight, listNum])

  /** 滑块高度*/
  const barHeight = useMemo(() => {
    let height = listVisualHeight.current / (listActualHeight) * listVisualHeight.current
    if (height < 20) {
      return height * (Math.floor(20 / height) + 1)
    } else {
      return height
    }
  }, [listActualHeight])

  /** 滑块最大可滚动距离*/
  const maxBarTransformY = useMemo(() => {
    if (scrollBar.current === null) {
      return listVisualHeight.current
    }
    return listVisualHeight.current - barHeight;
  }, [barHeight])

  /** 获取数据,初始化数据*/
  useEffect(() => {
    let list = []
    for (let i = 0; i < 10000; i++) {
      list.push({
        img: img1,
        content: `我是第${i}张图`
      })
    }
    setAllList(list);
  }, [])

  /** 初始化渲染数据*/
  useEffect(() => {
    let list = []
    for (let i = startInd; i < startInd + renderQuantity.current && i < allList.length; i++) {
      list.push(allList[i])
    }
    setResultList(list)
  }, [allList, startInd])

  /** 设置鼠标在滑块中点击的y轴坐标*/
  const onMousedown = useCallback((e: MouseEvent) => {
    e.preventDefault()
    document.documentElement.style.cursor = 'grabbing';
    mouseInBar.current.y = e.y - (scrollBar.current as HTMLDivElement).getBoundingClientRect().y
    isMove.current = true;
  }, [])

  const onMouseup = useCallback(() => {
    isMove.current = false
    document.documentElement.style.cursor = 'default';
  }, [])


  const onMousemove = useCallback((e: MouseEvent) => {
    if (isMove.current === false) {
      return
    }

    /** 滚动条移动距离*/
    let barTransformY = e.y - initPointerObj.current.y - mouseInBar.current.y;

    if (barTransformY > maxBarTransformY) {
      barTransformY = maxBarTransformY
    }
    if (barTransformY <= 0) {
      barTransformY = 0
    }

    /** 实际内容移动距离*/
    let listTransformY = barTransformY * (listActualHeight - listVisualHeight.current) / maxBarTransformY;
    let showInd = Math.floor(listTransformY / listItemDefaultHeight)

    setScrollBarStyle({
      transform: `translate(${0}px,${barTransformY}px)`
    })
    setListStyle({
      transform: `translate(${0}px,-${listTransformY - showInd * listItemDefaultHeight}px)`
    })

    setStartInd(showInd)
  }, [maxBarTransformY, startInd, listItemDefaultHeight])

  const onWheel = useCallback((e: WheelEvent) => {
    e.preventDefault()

    let transformValue = window.getComputedStyle(listDom.current as unknown as HTMLDivElement).getPropertyValue('transform');
    let translateYValue = 0;
    if (transformValue !== 'none') {
      const matrixValues = (transformValue as any).match(/matrix.*\((.+)\)/)[1].split(', ');
      translateYValue = parseInt(matrixValues[5], 10);
    }
    let listTransformY = startInd * listItemDefaultHeight + Math.abs(translateYValue);

    /** 判断是向上还是向下滚动*/
    const deltaY = e.deltaY;
    if (deltaY > 0) {
      listTransformY = startInd * listItemDefaultHeight + Math.abs(translateYValue) + 70;
      if (listTransformY > listActualHeight - listVisualHeight.current) {
        listTransformY = listActualHeight - listVisualHeight.current;
      }
    } else if (deltaY < 0) {
      listTransformY = startInd * listItemDefaultHeight + Math.abs(translateYValue) - 70;
      if (listTransformY < 0) {
        listTransformY = 0
      }
    }

    let barTransformY = listTransformY * maxBarTransformY / (listActualHeight - listVisualHeight.current);
    let showInd = Math.floor(listTransformY / listItemDefaultHeight)
    setScrollBarStyle({
      transform: `translate(${0}px,${barTransformY}px)`
    })
    setListStyle({
      transform: `translate(${0}px,-${listTransformY - showInd * listItemDefaultHeight}px)`
    })

    setStartInd(showInd)
  }, [startInd, listActualHeight, listItemDefaultHeight])

  /** 设置初始化滑块在页面中的y轴坐标*/
  const setInitPointer = useCallback(() => {
    if (frameDom.current === null) {
      return
    }
    let frameDomInfo = (frameDom.current as HTMLDivElement).getBoundingClientRect();
    initPointerObj.current.y = frameDomInfo.y;
  }, [])

  /** 初始化数据*/
  useEffect(() => {
    if (frameDom.current === null) {
      return
    }

    let scrollElement = (frameDom.current as any).closest('.scrollElement')

    setInitPointer()

    let frameDomInfo = (frameDom.current as HTMLDivElement).getBoundingClientRect();
    listVisualHeight.current = frameDomInfo.height;

    (scrollBar.current as HTMLDivElement).addEventListener('mousedown', onMousedown);
    window.addEventListener('resize', setInitPointer)
    scrollElement.addEventListener('scroll', setInitPointer)
    document.addEventListener('mouseup', onMouseup)

    return () => {
      scrollBar.current && (scrollBar.current as HTMLDivElement).removeEventListener('mousedown', onMousedown);
      document.removeEventListener('mouseup', onMouseup)
      window.removeEventListener('resize', setInitPointer)
      scrollElement.removeEventListener('scroll', setInitPointer)
    }
  }, [])


  useEffect(() => {
    if (frameDom.current === null) {
      return
    }
    document.addEventListener('mousemove', onMousemove);
    (frameDom.current as HTMLDivElement).addEventListener('wheel', onWheel);

    return () => {
      frameDom.current && (frameDom.current as unknown as HTMLDivElement).removeEventListener('wheel', onWheel);
      document.removeEventListener('mousemove', onMousemove)
    }
  }, [onWheel, onMousemove])

  return (
    <>
      <FrameSty ref={frameDom}>
        <ListSty ref={listDom}
          style={
            {
              ...listStyle,
              height: listActualHeight + 'px'
            }}
        >
          {
            resultList.map((item, ind) => {
              return (
                <ListItemSty key={ind}>
                  <img src={item.img} alt="" />
                  <div>{item.content}</div>
                </ListItemSty>
              )
            })
          }
        </ListSty>
        <ListScrollBar ref={scrollBar} draggable={false}
          style={{
            ...scrollBarStyle,
            height: `${barHeight}px`
          }} >
        </ListScrollBar>
      </FrameSty>
    </>
  )
}